﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace ExpressionLanguage
{
    public static class CoreLibrary
    {
        
        private static bool Core_ToNumber(List<ExpressionInterpreter.ExecutionStack.Variable> parameters, out ExpressionInterpreter.ExecutionStack.Variable result)
        {
            result = default(ExpressionInterpreter.ExecutionStack.Variable);
            if (parameters.Count != 1)
                return false;
            // This function will try as much as possible, no matter what the type of that variable is.
            switch( parameters[0].Type )
            {
                case ExpressionInterpreter.ExecutionStack.Variable.VariableType.NUMBER:
                    result = parameters[0];
                    return true;
                case ExpressionInterpreter.ExecutionStack.Variable.VariableType.STRING:
                    string str;
                    if (!parameters[0].AsString(out str))
                        return false;
                    try
                    {
                        double number = (double)Convert.ToDouble(str);
                        result = new ExpressionInterpreter.ExecutionStack.Variable(number);
                    }
                    catch (Exception)
                    {
                        return false;
                    }
                    return true;
                case ExpressionInterpreter.ExecutionStack.Variable.VariableType.OBJECT:
                    object obj = parameters[0].AsObject();
                    if (obj == null)
                        return false;
                    try
                    {
                        result = new ExpressionInterpreter.ExecutionStack.Variable((double)Convert.ChangeType(obj, typeof(double)));
                    }
                    catch (Exception)
                    {
                        return false;
                    }
                    return true;
                default:
                    return false;
            }
        }
        private static bool Core_ToString(List<ExpressionInterpreter.ExecutionStack.Variable> parameters, out ExpressionInterpreter.ExecutionStack.Variable result)
        {
            result = default(ExpressionInterpreter.ExecutionStack.Variable);
            if (parameters.Count != 1)
                return false;
            // This function will try as much as possible, no matter what the type of that variable is.
            switch (parameters[0].Type)
            {
                case ExpressionInterpreter.ExecutionStack.Variable.VariableType.STRING:
                    result = parameters[0];
                    return true;
                case ExpressionInterpreter.ExecutionStack.Variable.VariableType.NUMBER:
                    double number;
                    if (!parameters[0].AsNumber(out number))
                        return false;
                    try
                    {
                        result = new ExpressionInterpreter.ExecutionStack.Variable(number.ToString());
                    }
                    catch (Exception)
                    {
                        return false;
                    }
                    return true;
                case ExpressionInterpreter.ExecutionStack.Variable.VariableType.OBJECT:
                    object obj = parameters[0].AsObject();
                    if (obj == null)
                        return false;
                    try
                    {
                        result = new ExpressionInterpreter.ExecutionStack.Variable((string)Convert.ChangeType(obj, typeof(string)));
                    }
                    catch (Exception)
                    {
                        return false;
                    }
                    return true;
                default:
                    return false;
            }
        }
        private static bool Core_ToObject(List<ExpressionInterpreter.ExecutionStack.Variable> parameters, out ExpressionInterpreter.ExecutionStack.Variable result)
        {
            result = default(ExpressionInterpreter.ExecutionStack.Variable);
            if (parameters.Count != 1)
                return false;
            result = parameters[0];
            switch(parameters[0].Type)
            {
                case ExpressionInterpreter.ExecutionStack.Variable.VariableType.NUMBER:
                    double num;
                    if (!parameters[0].AsNumber(out num))
                        return false;
                    result = new ExpressionInterpreter.ExecutionStack.Variable((object)(num));
                    return true;
                case ExpressionInterpreter.ExecutionStack.Variable.VariableType.STRING:
                    string str;
                    if (!parameters[0].AsString(out str))
                        return false;
                    result = new ExpressionInterpreter.ExecutionStack.Variable((object)(str));
                    return true;
                case ExpressionInterpreter.ExecutionStack.Variable.VariableType.OBJECT:
                    result = parameters[0];
                    return true;
                default:
                    return false;
            }
            
        }
        private static bool Core_Print(List<ExpressionInterpreter.ExecutionStack.Variable> parameters, out ExpressionInterpreter.ExecutionStack.Variable result)
        {
            result = ExpressionInterpreter.ExecutionStack.Variable.MakeVoid();
            string strval;
            double dval;
            object udata;
            foreach (var entry in parameters)
            {
                switch (entry.Type)
                {
                    case ExpressionInterpreter.ExecutionStack.Variable.VariableType.STRING:
                        entry.AsString(out strval);
                        Console.Write(strval);
                        break;
                    case ExpressionInterpreter.ExecutionStack.Variable.VariableType.NUMBER:
                        entry.AsNumber(out dval);
                        Console.Write(dval);
                        break;
                    case ExpressionInterpreter.ExecutionStack.Variable.VariableType.VOID:
                        Console.Write("<VOID>");
                        break;
                    case ExpressionInterpreter.ExecutionStack.Variable.VariableType.OBJECT:
                        udata = entry.AsObject();
                        if( udata != null )
                            Console.Write(udata.ToString());
                        break;
                    default:
                        break;
                }
            }
            Console.Write('\n');
            return true;
        }
        public static void LoadLibrary( ExpressionInterpreter.GlobalFunction func, ExpressionInterpreter.ExecutionStack stack )
        {
            func.RegisterGlobalFunction("print", CoreLibrary.Core_Print);
            func.RegisterGlobalFunction("tonumber", CoreLibrary.Core_ToNumber);
            func.RegisterGlobalFunction("tostring", CoreLibrary.Core_ToString);
            func.RegisterGlobalFunction("toobject", CoreLibrary.Core_ToObject);
            stack.Add("null",ExpressionInterpreter.ExecutionStack.Variable.MakeVoid());
            stack.Add("true", new ExpressionInterpreter.ExecutionStack.Variable(1.0));
            stack.Add("false", new ExpressionInterpreter.ExecutionStack.Variable(0.0));
        }
    }
}
